using System;
using System.Collections;
using System.Collections.Generic;

namespace EventReactor
{
    public abstract class ModuleHub : IModuleHub, IModuleHubInitView, IModuleHubAllView,
        IModuleHubModuleListenView, IModuleHubModuleInitView, IModuleHubModuleQueryView,
        IModuleHubModulePackageView, IModuleHubInteractorView
    {
        protected Dictionary<string, IModulePackage> _packageDic;
        protected IEventBus _bus;
        protected event Action<object, IEvent> OnAnyEventSent;
        public ModuleHub()
        {
            _packageDic = new Dictionary<string, IModulePackage>();
            _bus = new EventBus();
        }
        public abstract void OnInit(IModuleHubInitView hub, Action<IModuleHubAllView> onEnd);
        public virtual void OnDestroy()
        {
            _bus = null;
            foreach (var imodule in _packageDic.Values)
            {
                imodule.OnDestroy();
            }
            _packageDic = null;
        }
        private IRemove Add(IModulePackage package)
        {
            var key = package.Key;
            _packageDic[key] = package;
            return new Rmv(() =>
            {
                package.OnDestroy();
                _packageDic.Remove(key);
            });
        }
        public IRemove RuntimeAdd(object sourceScript,IModulePackage package,Action onEnd)
        {
            var key = package.Key;
            bool success = !_packageDic.ContainsKey(key);

            var result = new OnRumtineAddModulePackage()
            {
                MainValue = package,
                isSuccess = success,
            };
            if (success)
            {
                _packageDic[key] = package;
                package.OnInit(this, () => { onEnd(); TrySend(sourceScript, result, null); });
                return new Rmv(() =>
                {
                    package.OnDestroy();
                    _packageDic.Remove(key);
                });
            }
            else
            {
                TrySend(sourceScript, result, null);
                return null;
            }
        }
        private void Start(Action<IModuleHubAllView> onEnd)
        {
            var countdown = new ActionCountdown(_packageDic.Count, () => onEnd?.Invoke(this));

            //固定模块组以免在未初始化完时被动态添加的模块组影响
            var flattened = new IModulePackage[_packageDic.Count];
            _packageDic.Values.CopyTo(flattened, 0);

            foreach (var package in flattened)
            {
                package.OnInit(this, countdown.Signal);
            }
        }
        private IRemove Init(IInteractor interactor,Action<IInteractor> onEnd)
        {
            return interactor.OnInit(this,onEnd);
        }
        /// <summary>
        /// 尝试发送请求
        /// </summary>
        private bool TryOrdered(object sender, IOrderedAsyncEvent evt, Action<IOrderedAsyncEvent> onComplete)
        {
            OnAnyEventSent?.Invoke(sender, evt);
            return _bus.TrySendOrdered(sender, evt, onComplete);
        }
        private bool TryWhenAll(object sender, IWhenAllAsyncEvent evt, Action<IWhenAllAsyncEvent> onComplete)
        {
            OnAnyEventSent?.Invoke(sender, evt);
            return _bus.TrySendWhenAll(sender, evt, onComplete);
        }
        /// <summary>
        /// 顺序发送一个异步请求
        /// </summary>
        private bool TryRequestOrdered<TResult>(object sender, IOrderedRequest<TResult> evt, Action<TResult> onComplete) where TResult:IResult,new()
        {
            return TryOrdered(sender, evt, raw =>
            {
                if (raw is not IOrderedRequest<TResult> e) return;
                TrySend(sender, e.Result, null);
                onComplete?.Invoke(e.Result);
            });
        }
        /// <summary>
        /// 不要求顺序发送一个异步请求
        /// </summary>
        private bool TryRequestWhenAll<TResult>(object sender, IWhenAllRequest<TResult> evt, Action<TResult> onComplete) where TResult : IResult, new()
        {
            return TryWhenAll(sender, evt, raw =>
            {
                if (raw is not IWhenAllRequest<TResult> e) return;
                TrySend(sender, e.Result, null);
                onComplete?.Invoke(e.Result);
            });
        }
        private bool TrySend<TSync>(object sender, TSync evt, Action<TSync> onComplete) where TSync : ISyncEvent
        {
            OnAnyEventSent?.Invoke(sender, evt);
            return _bus.TrySend(sender, evt, raw =>
            {
                if (raw is not TSync e) return;
                onComplete?.Invoke(e);
            });
        }
        IRemove IModuleHubInitView.Add(IModulePackage module) => Add(module);
        void IModuleHubInitView.Start(Action<IModuleHubAllView> onEnd) => Start(onEnd);

        IRemove ICanListenAsync.Listen<TArgs>(int priority, Func<object, TArgs, EndMark> callback)
        {
            var type = typeof(TArgs);
            EndMark Wrap(object sender, IEvent evt)
            {
                if (evt is TArgs args) return callback(sender, args);
                return EndMark.End;
            }
            return _bus.Listen(type, priority, Wrap);
        }
        IRemove ICanInitInteractor.Init(IInteractor interactor, Action<IInteractor> onEnd) => Init(interactor,onEnd);
        bool ICanQuery.TryQuery<TQuery>(object sender, TQuery evt, Action<TQuery> onComplete) => TrySend(sender, evt, onComplete);
        private IRemove Listen<TArgs>(int priority, Action<object, TArgs> callback) where TArgs:IEvent
        {
            var type = typeof(TArgs);
            EndMark Wrap(object sender, IEvent evt)
            {
                if (evt is TArgs args) callback(sender, args);
                return EndMark.End;
            }
            return _bus.Listen(type, priority, Wrap);
        }
        IRemove ICanListenSync.Listen<TSyncEvent>(int priority, Action<object, TSyncEvent> callback) => Listen(priority, callback);
        bool ICanSendRequest.TryRequestOrdered<TResult>(object sender, IOrderedRequest<TResult> evt, Action<TResult> onComplete) => TryRequestOrdered(sender, evt, onComplete);
        bool ICanSendRequest.TryRequestWhenAll<TResult>(object sender, IWhenAllRequest<TResult> evt, Action<TResult> onComplete) => TryRequestWhenAll(sender, evt, onComplete);
        IModuleHubAllView IModuleHubModuleListenView.InListen(IEvent listenEnvironment) => this;
    }
    /// <summary>
    /// 同类型的模块包最多拥有一个实例
    /// </summary>
    public class ModulePackage : IModulePackage,IEnumerable<IModule>
    {
        private string _key;
        public string Key => _key;
        private Dictionary<Type, IModule> _moduleDic;
        public ModulePackage(string key) 
        {
            _moduleDic = new Dictionary<Type, IModule>();
            _key = key;
        }
        void IModulePackage.OnInit(IModuleHubModulePackageView hub,Action onEnd)
        {
            var countdown = new ActionCountdown(_moduleDic.Count, () =>
             {
                 foreach (var module in _moduleDic.Values)
                 {
                     module.OnQuery(hub);
                 }
                 onEnd?.Invoke();
             });
            foreach (var module in _moduleDic.Values)
            {
                module.OnListen(hub);
                module.OnInit(hub, countdown.Signal);
            }
        }
        IRemove IModulePackage.Add(IModule module)
        {
            var keyType = module.GetType();
            _moduleDic[keyType] = module;
            return new Rmv(() =>
            {
                module.OnDestroy();
                _moduleDic.Remove(keyType);
            });
        }
        public void Add(IModule module)
        {
            var keyType = module.GetType();
            _moduleDic[keyType] = module;
        }
        public void Add<TModule>()where TModule:IModule,new()
        {
            var module = new TModule();
            var keyType = module.GetType();
            _moduleDic[keyType] = module;
        }
        void IModulePackage.OnDestroy()
        {
            foreach(var module in _moduleDic.Values)
            {
                module.OnDestroy();
            }
            _moduleDic = null;
        }

        public IEnumerator<IModule> GetEnumerator() => _moduleDic.Values.GetEnumerator();
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }

    public abstract class Module : IModule
    {
        protected Rmv _rmvs;
        protected virtual void OnDestroy()
        {
            _rmvs?.Remove();
            _rmvs = null;
        }
        protected abstract void OnListen(IModuleHubModuleListenView hub);
        protected virtual void OnInit(IModuleHubModuleInitView hub, Action onEnd) { onEnd?.Invoke(); }
        protected virtual void OnQuery(IModuleHubModuleQueryView hub) { }
        void IModule.OnListen(IModuleHubModuleListenView hub) => OnListen(hub);
        void IModule.OnInit(IModuleHubModuleInitView hub,Action onEnd) => OnInit(hub,onEnd);
        void IModule.OnQuery(IModuleHubModuleQueryView hub) => OnQuery(hub);
        void IModule.OnDestroy() => OnDestroy();
    }
    public abstract class Interactor:IInteractor
    {
        protected Rmv _rmvs;
        protected abstract void OnInit(IModuleHubInteractorView hub,Action<IInteractor> onEnd);
        protected virtual void OnDestroy()
        {
            _rmvs?.Remove();
            _rmvs = null;
        }
        IRemove IInteractor.OnInit(IModuleHubInteractorView hub, Action<IInteractor> onEnd)
        {
            OnInit(hub, onEnd);
            return new Rmv(OnDestroy);
        }
        void IInteractor.OnDestroy() => OnDestroy();
    }
    public class OnRumtineAddModulePackage : Result<IModulePackage> { }
}
